package com.fdb.basic.framework.core.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 通过Java的Zip输入输出流实现压缩和解压文件
 * 
 * @author ludayong
 * @since 2019年1月2日 上午9:02:13
 * @version 1.0
 */
public final class ZipUtil {

	private static Logger logger = LoggerFactory.getLogger(ZipUtil.class);

	public static final String EXT = ".zip";

	private static final int BUFFER = 4096;

	private ZipUtil() {

	}

	/**
	 * 压缩文件
	 * 
	 * @param filePath
	 *            待压缩的文件路径
	 * @return 压缩后的文件
	 */
	public static File zip(String filePath) {
		File target = null;
		File source = new File(filePath);
		if (source.exists()) {
			// 压缩文件名=源文件名+EXT
			String zipName = source.getName() + EXT;
			if(source.isDirectory()) {
				target = new File(source.getAbsoluteFile(), zipName);
			}else {
				target = new File(source.getParent(), zipName);
			}
			if (target.exists()) {
				target.delete(); // 删除旧的文件
			}
			FileOutputStream fos = null;
			ZipOutputStream zos = null;
			try {
				fos = new FileOutputStream(target);
				zos = new ZipOutputStream(new BufferedOutputStream(fos));
				// 添加对应的文件Entry
				addEntry("", source, zos);
			} catch (IOException e) {
				logger.error("压缩文件出现异常：" + e.getMessage());
				e.printStackTrace();
				throw new RuntimeException(e);
			} finally {
				if (zos != null) {
					try {
						zos.closeEntry();
						zos.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (fos != null) {
					try {
						fos.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				
			}
		}
		return target;
	}

	/**
	 * 扫描添加文件Entry
	 * 
	 * @param base
	 *            基路径
	 * 
	 * @param source
	 *            源文件
	 * @param zos
	 *            Zip文件输出流
	 * @throws IOException
	 */
	private static void addEntry(String base, File source, ZipOutputStream zos) throws IOException {
		// 按目录分级，形如：/aaa/bbb.txt
		String entry = base + source.getName();
		if (source.isDirectory()) {
			for (File file : source.listFiles()) {
				// 递归列出目录下的所有文件，添加文件Entry
				if (!file.getName().endsWith(EXT)) {
					addEntry(entry + "/", file, zos);
				}
			}
		} else {
			FileInputStream fis = null;
			BufferedInputStream bis = null;
			try {
				byte[] buffer = new byte[1024 * 10];
				fis = new FileInputStream(source);
				bis = new BufferedInputStream(fis, buffer.length);
				int read = 0;
				zos.putNextEntry(new ZipEntry(entry));
				while ((read = bis.read(buffer, 0, buffer.length)) != -1) {
					zos.write(buffer, 0, read);
				}
//				zos.closeEntry();
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				closeQuietly(bis, fis);
			}
		}
	}

	/**
	 * 解压文件
	 * 
	 * @param filePath
	 *            压缩文件路径
	 */
	public static boolean unzip(String filePath, String savePath) {
		boolean result = false;
		File source = new File(filePath);
		if (source.exists()) {
			File savePathFile = new File(savePath);
			if (!savePathFile.isDirectory()) {
				savePathFile.mkdirs();
			}
			ZipInputStream zis = null;
			BufferedOutputStream bos = null;
			try {
				zis = new ZipInputStream(new FileInputStream(source));
				ZipEntry entry = null;
				while ((entry = zis.getNextEntry()) != null) {
					String currentEntry = entry.getName();
					String path = savePath + entry.getName();
					File unzipFile = new File(path);
					if (entry.isDirectory()) {
						if (!unzipFile.isDirectory()) {
							unzipFile.mkdirs();
						}
					} else {
						// 写入文件
						bos = new BufferedOutputStream(new FileOutputStream(path));
						int read = 0;
						byte[] buffer = new byte[1024 * 10];
						while ((read = zis.read(buffer, 0, buffer.length)) != -1) {
							bos.write(buffer, 0, read);
						}
						bos.flush();
						zis.closeEntry();
					}

					if (currentEntry.endsWith(EXT)) {
						unzip(path, savePath);
						System.out.println(path);
					}
				}
				result = true;
			} catch (IOException e) {
				logger.error("解压文件出现异常：" + e.getMessage());
				e.printStackTrace();
				throw new RuntimeException(e);
			} finally {
				if (bos != null) {
					try {
						bos.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (zis != null) {
					try {
						zis.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return result;
	}

	/**
	 * 关闭一个或多个流对象
	 * 
	 * @param closeables
	 *            可关闭的流对象列表
	 * @throws IOException
	 */
	public static void close(Closeable... closeables) throws IOException {
		if (closeables != null) {
			for (Closeable closeable : closeables) {
				if (closeable != null) {
					closeable.close();
				}
			}
		}
	}

	/**
	 * 移动目录
	 * 
	 * @param srcDirName
	 *            源目录完整路径
	 * @param destDirName
	 *            目的目录完整路径
	 * @return 目录移动成功返回true，否则返回false
	 */
	public static boolean moveDirectory(String srcDirName, String destDirName) {

		File srcDir = new File(srcDirName);
		if (!srcDir.exists() || !srcDir.isDirectory())
			return false;

		File destDir = new File(destDirName);
		if (!destDir.exists()) {
			destDir.mkdirs();
		} else {
			deleteFolder(destDirName);
		}

		/**
		 * 如果是文件则移动，否则递归移动文件夹。删除最终的空源文件夹 注意移动文件夹时保持文件夹的树状结构
		 */
		File[] sourceFiles = srcDir.listFiles();
		for (File sourceFile : sourceFiles) {
			if (sourceFile.isFile())
				moveFile(sourceFile.getAbsolutePath(), destDir.getAbsolutePath());
			else if (sourceFile.isDirectory())
				moveDirectory(sourceFile.getAbsolutePath(), destDir.getAbsolutePath() + File.separator + sourceFile.getName());
			else
				;
		}
		return srcDir.delete();
	}

	/**
	 * 移动文件
	 * 
	 * @param srcFileName
	 *            源文件完整路径
	 * @param destDirName
	 *            目的目录完整路径
	 * @return 文件移动成功返回true，否则返回false
	 */
	public static boolean moveFile(String srcFileName, String destDirName) {

		File srcFile = new File(srcFileName);
		if (!srcFile.exists() || !srcFile.isFile())
			return false;

		File destDir = new File(destDirName);
		if (!destDir.exists())
			destDir.mkdirs();

		return srcFile.renameTo(new File(destDirName + File.separator + srcFile.getName()));
	}

	/**
	 * 移动文件
	 * 
	 * @param srcFileName
	 *            源文件完整路径 <<<<<<< HEAD ======= >>>>>>> update >>>>>>> update =======
	 *            >>>>>>> 更新方法
	 * @param destDir
	 *            目的目录
	 * @param newFileName
	 *            新文件名
	 * @return 文件移动成功返回true，否则返回false
	 */
	public static boolean moveFile(String srcFileName, String destPath, String newFileName) {

		File srcFile = new File(srcFileName);
		if (!srcFile.exists() || !srcFile.isFile())
			return false;

		File destDir = new File(destPath);
		if (!destDir.exists())
			destDir.mkdirs();

		File destFile = new File(destPath + File.separator + newFileName);
		if (destFile.exists())
			destFile.delete();

		return srcFile.renameTo(new File(destPath + File.separator + newFileName));
	}

	/**
	 * 移动文件
	 * 
	 * @param srcFileName
	 *            源文件完整路径
	 * @param destDir
	 *            目的目录
	 * @param newFileName
	 *            新文件名
	 * @param delFlag
	 *            是否删除源文件
	 * @return 文件移动成功返回true，否则返回false
	 */
	public static boolean moveFile(String srcFileName, String destPath, String newFileName, boolean delFlag) {

		File srcFile = new File(srcFileName);
		if (!srcFile.exists() || !srcFile.isFile())
			return false;

		File destDir = new File(destPath);
		if (!destDir.exists())
			destDir.mkdirs();

		File destFile = new File(destPath + File.separator + newFileName);
		if (destFile.exists() && delFlag)
			destFile.delete();

		return srcFile.renameTo(new File(destPath + File.separator + newFileName));
	}

	/**
	 * 删除目录（文件夹）以及目录下的文件
	 * 
	 * @param sPath
	 *            被删除目录的文件路径
	 * @return 目录删除成功返回true，否则返回false
	 */
	public static boolean deleteDirectory(String sPath) {
		// 如果sPath不以文件分隔符结尾，自动添加文件分隔符
		if (!sPath.endsWith(File.separator)) {
			sPath = sPath + File.separator;
		}
		File dirFile = new File(sPath);
		// 如果dir对应的文件不存在，或者不是一个目录，则退出
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return false;
		}
		boolean flag = true;
		// 删除文件夹下的所有文件(包括子目录)
		File[] files = dirFile.listFiles();
		for (int i = 0; i < files.length; i++) {
			// 删除子文件
			if (files[i].isFile()) {
				flag = deleteFile(files[i].getAbsolutePath());
				if (!flag)
					break;
			} // 删除子目录
			else {
				flag = deleteDirectory(files[i].getAbsolutePath());
				if (!flag)
					break;
			}
		}
		if (!flag)
			return false;
		// 删除当前目录
		if (dirFile.delete()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 删除单个文件
	 * 
	 * @param sPath
	 *            被删除文件的文件名
	 * @return 单个文件删除成功返回true，否则返回false
	 */
	public static boolean deleteFile(String sPath) {
		boolean flag = false;
		File file = new File(sPath);
		// 路径为文件且不为空则进行删除
		if (file.isFile() && file.exists()) {
			file.delete();
			flag = true;
		}
		return flag;
	}

	/**
	 * 根据路径删除指定的目录或文件，无论存在与否
	 * 
	 * @param sPath
	 *            要删除的目录或文件
	 * @return 删除成功返回 true，否则返回 false。
	 */
	public static boolean deleteFolder(String sPath) {
		boolean flag = false;
		File file = new File(sPath);
		// 判断目录或文件是否存在
		if (!file.exists()) { // 不存在返回 false
			return flag;
		} else {
			// 判断是否为文件
			if (file.isFile()) { // 为文件时调用删除文件方法
				return deleteFile(sPath);
			} else { // 为目录时调用删除目录方法
				return deleteDirectory(sPath);
			}
		}
	}

	/**
	 * 关闭一个或多个流对象
	 * 
	 * @param closeables
	 *            可关闭的流对象列表
	 */
	public static void closeQuietly(Closeable... closeables) {
		try {
			close(closeables);
		} catch (IOException e) {
			logger.error("关闭资源出现异常：" + e.getMessage());
			e.printStackTrace();
		}
	}

	/**
	 * 解压文件
	 * 
	 * @param source
	 *            解压缩文件
	 * @param destination
	 *            解压缩文件路径
	 * 
	 */
	public static boolean unZipAll(File source, File destination) throws IOException {

		boolean result = false;
		logger.info("解压文件：" + source.getName());
		ZipFile zip = new ZipFile(source);
		try {
			destination.getParentFile().mkdirs();
			Enumeration<?> zipFileEntries = zip.entries();

			while (zipFileEntries.hasMoreElements()) {
				ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
				String currentEntry = entry.getName();
				File destFile = new File(destination, currentEntry);

				File destinationParent = destFile.getParentFile();
				if (!destinationParent.exists()) {
					destinationParent.mkdirs();
				}
				if (!entry.isDirectory()) {
					BufferedInputStream is = null;
					FileOutputStream fos = null;
					BufferedOutputStream dest = null;
					try {
						is = new BufferedInputStream(zip.getInputStream(entry));
						int currentByte;
						byte data[] = new byte[BUFFER];

						fos = new FileOutputStream(destFile);
						dest = new BufferedOutputStream(fos, BUFFER);

						while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
							dest.write(data, 0, currentByte);
						}
					} catch (Exception e) {
						logger.error("解压文件出错：" + source.getName());
						e.printStackTrace();
						throw e;
					} finally {
						if (dest != null) {
							dest.close();
						}
						if (fos != null) {
							fos.close();
						}
						if (is != null) {
							is.close();
						}
					}
				} else {
					destFile.mkdirs();
				}

				if (currentEntry.endsWith(".zip")) {
					// 如果是压缩文件，继续解压缩
					unZipAll(destFile, destinationParent);
					if (!destFile.delete()) {
						logger.error("删除临时文件出错：" + destFile.getName());
					}
				}
				result = true;
			}
		} catch (Exception e) {
			logger.error("解压文件文件出错：" + source.getName());
			e.printStackTrace();
		} finally {
			zip.close();
		}
		logger.info("解压缩文件文件成功：" + source.getName());
		return result;
	}

	/**
	 * 解压文件
	 * 
	 * @param source
	 *            解压缩文件
	 * @param destination
	 *            解压缩文件路径
	 * 
	 */
	public static boolean unZipSig(File source, File destination) throws IOException {

		boolean result = false;
		logger.info("解压文件：" + source.getName());
		ZipFile zip = new ZipFile(source);
		try {
			destination.getParentFile().mkdirs();
			Enumeration<?> zipFileEntries = zip.entries();

			while (zipFileEntries.hasMoreElements()) {
				ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
				String currentEntry = entry.getName();
				File destFile = new File(destination, currentEntry);

				File destinationParent = destFile.getParentFile();
				if (!destinationParent.exists()) {
					destinationParent.mkdirs();
				}
				if (!entry.isDirectory()) {
					BufferedInputStream is = null;
					FileOutputStream fos = null;
					BufferedOutputStream dest = null;
					try {
						is = new BufferedInputStream(zip.getInputStream(entry));
						int currentByte;
						byte data[] = new byte[BUFFER];

						fos = new FileOutputStream(destFile);
						dest = new BufferedOutputStream(fos, BUFFER);

						while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
							dest.write(data, 0, currentByte);
						}
					} catch (Exception e) {
						logger.error("解压文件出错：" + source.getName());
						e.printStackTrace();
						throw e;
					} finally {
						if (dest != null) {
							dest.close();
						}
						if (fos != null) {
							fos.close();
						}
						if (is != null) {
							is.close();
						}
					}
				} else {
					destFile.mkdirs();
				}

				result = true;
			}
		} catch (Exception e) {
			logger.error("解压文件文件出错：" + source.getName());
			e.printStackTrace();
		} finally {
			zip.close();
		}
		logger.info("解压缩文件文件成功：" + source.getName());
		return result;
	}

	/**
	 * 解压文件
	 * 
	 * @param source
	 *            解压缩文件
	 * @param destination
	 *            解压缩文件路径
	 * @param isMoveFile
	 *            是否对解压出来的文件夹内的文件移动到上级目录
	 * 
	 */
	public static boolean unZipAll(File source, File destination, boolean isMoveFile) throws IOException {
		boolean result = false;
		boolean unZipResult = unZipAll(source, destination);// 解压文件
		if (unZipResult && isMoveFile) {
			// 1、解压影像文件
			File[] destFiles = destination.listFiles();
			if (destFiles.length > 0) {
				for (File destFile : destFiles) {
					if (destFile.isDirectory()) {// 解压目标路径下有文件夹，对文件夹内的文件移动到上级目录
						try {
							File[] desFiles = destFile.listFiles();
							if (desFiles.length > 0) {
								for (File desFile : desFiles) {
									String movePath = destFile.getParent() + File.separator;
									moveFile(desFile.getAbsolutePath(), movePath, desFile.getName(), true);
								}
							}
						} catch (Exception e) {
							result = false;
							logger.error("移动文件出错：" + destFile.getName());
							break;
						} finally {
							destFile.delete();
						}
					} else {
						continue;
					}
				}
			}
			result = true;
		}
		return result;
	}
}